package com.nefu.softlab.xjwc.utils;

import com.github.junrar.Archive;
import com.github.junrar.rarfile.FileHeader;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import static com.nefu.softlab.xjwc.common.Constant.UPLOAD_PATH_PREFIX;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author LiXiwen
 * @date 2020/2/8 15:29
 */
public class FileUtil {
    private static LoggerUtil logger = new LoggerUtil();

    static final Executor executor = Executors.newFixedThreadPool(10, (Runnable r) -> {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    });

    public static boolean uploadFileSync(MultipartFile file, String path) {
        return uploadFile(file, path);
    }

    public static Future<Boolean> uploadFileAsync(MultipartFile file, String path) {
        return CompletableFuture.supplyAsync(() -> uploadFile(file, path), executor);
    }

    /**
     * 如果是普通文件，直接放到项目文件夹内，如果是压缩包，把压缩包内文件解压到项目文件夹内
     *
     * @param file 上传的文件，一般是压缩包，仅支持 .zip 压缩包的解压
     * @param path 项目文件夹路径
     * @return
     */
    private static boolean uploadFile(MultipartFile file, String path) {
        // 包含后缀名的名字
        String filename = file.getOriginalFilename();

        // 上传的压缩包所在路径全名
        String filePath = path + filename;
        System.err.println("uploadFile ---> filename : " + filename + " , filePath : " + filePath + " , ");

        File serverFile = new File(path + filename);
        //将上传的文件写入到服务器端文件内, 如果是普通文件，直接写入了，如果是压缩包，会在下面解压缩。
        try {
            file.transferTo(serverFile);
        } catch (IOException e) {
            System.out.println("上传文件失败 ：");
            e.printStackTrace();
            logger.error("上传文件失败 ：" + e.getMessage());
            return false;
        }

        // 不包含后缀名的文件名
        String prefixName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));

        // 解压路径
        String descPath = path + prefixName;

        // 后缀名
        String postfix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));

        if (null != postfix) {
            System.err.println("后缀名 postfix : " + postfix);
            if (".zip".equals(postfix)) {
                executor.execute(new Thread(() -> {
                    try {
                        // 休眠20s, 保证压缩包一定是上传完毕再去解压
                        // Thread.sleep(20000);
                        // 执行解压缩操作，并把所有文件放到一个目录下面，删除原压缩包和文件
                        unzipOperate(filePath, descPath, path);
                    } catch (Exception e) {
                        System.err.println("unzip : " + e.getMessage());
                        return;
                    }
                }));
            } else {
                System.err.println("暂不支持非.zip压缩包的解压, 文件后缀名 postfix --> " + postfix);
                if (isExcel(postfix) || isPPT(postfix) || isDoc(postfix)) {
                    try {
                        OfficeUtil.File2PDFOrHtml(filePath, "pdf");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return true;
    }


    /**
     * 解压缩的一系列操作
     *
     * @param filePath 压缩文件全路径名
     * @param descPath 解压缩文件夹路径
     * @param path 项目文件夹路径
     */
    public static void unzipOperate(String filePath, String descPath, String path) {
        // 解压缩文件，因为unzip方法改了，所以这里第二个为path
        unZipFiles(new File(filePath), path);

        // 获取解压缩文件夹下的所有文件
        List<File> allFile = getAllFile(descPath);
        // 复制到项目文件夹下
        allFile.parallelStream().forEach(file
                -> copyfile(file.getAbsolutePath(), path + file.getName()));

        // 删除原先的压缩包和直接解压的压缩包文件夹
        delAllFile(new File(filePath));
        delAllFile(new File(descPath));

        // 获取该项目文件夹下所有文件
        List<File> allFile1 = getAllFile(path);

        // 把所有符合条件的文件转换为pdf文件，方便预览
        executor.execute(new Thread(() ->
                allFile1.parallelStream().forEach(file -> {
                    try {
                        String fileName = file.getName();
                        // 后缀名
                        String postFix = fileName.substring(fileName.lastIndexOf("."));
                        // 只有这三种才会转换
                        if (isDoc(postFix) || isExcel(postFix) || isPPT(postFix)) {
                            OfficeUtil.File2PDFOrHtml(file.getAbsolutePath(), "pdf");
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                })
        ));
    }

    private static boolean isPPT(String postFix) {
        postFix = postFix.toLowerCase();
        return ".ppt".equals(postFix) || ".pptx".equals(postFix);
    }

    private static boolean isDoc(String postFix) {
        postFix = postFix.toLowerCase();
        return ".doc".equals(postFix) || ".docx".equals(postFix);
    }

    private static boolean isExcel(String postFix) {
        postFix = postFix.toLowerCase();
        return ".xls".equals(postFix) || ".xlsx".equals(postFix);
    }

    private static boolean isVideo(String postFix) {
        postFix = postFix.toLowerCase();
        return ".mp4".equals(postFix);
    }

    private static boolean isPdf(String postFix) {
        postFix = postFix.toLowerCase();
        return ".pdf".equals(postFix);
    }

    private static boolean isPicture(String postFix) {
        postFix = postFix.toLowerCase();
        return ".jpeg".equals(postFix.toLowerCase()) || ".bmp".equals(postFix) || ".jpg".equals(postFix) || ".png".equals(postFix) || ".tif".equals(postFix) || ".gif".equals(postFix) || ".pcx".equals(postFix) || ".tga".equals(postFix) || ".exif".equals(postFix) || ".fpx".equals(postFix) || ".svg".equals(postFix) || ".psd".equals(postFix) || ".cdr".equals(postFix) || ".pcd".equals(postFix) || ".dxf".equals(postFix) || ".ufo".equals(postFix) || ".eps".equals(postFix) || ".ai".equals(postFix) || ".raw".equals(postFix) ||  ".wmf".equals(postFix) || ".webp".equals(postFix);
    }

    public static boolean deleteDocument(String path) {
        String filepath = UPLOAD_PATH_PREFIX + path + "/";
        System.out.println("deleteDocument ---> filepath : " + filepath);
        File file = new File(filepath);
        if (file.exists()) {
            if (delAllFile(new File(path))) {
                return true;
            }
        }
        return false;
    }

    public static boolean delAllFile(File directory){
        if (!directory.isDirectory()){
            System.out.println("删除" + directory.getAbsolutePath());
            directory.delete();
        } else {
            File [] files = directory.listFiles();

            // 空文件夹
            if (files.length == 0){
                directory.delete();
                System.out.println("删除" + directory.getAbsolutePath());
                return true;
            }

            // 删除子文件夹和子文件
            for (File file : files){
                if (file.isDirectory()){
                    delAllFile(file);
                } else {
                    file.delete();
                    System.out.println("删除" + file.getAbsolutePath());
                }
            }

            // 删除文件夹本身
            directory.delete();
            System.out.println("删除" + directory.getAbsolutePath());
        }
        return true;
    }


    public static void mkdir(String dirName) {
        String dirPath = UPLOAD_PATH_PREFIX + dirName + "/";
        File dir = new File(dirPath);
        if (!dir.exists()) {
            System.out.println("FileUtil.mkdir() --> : " + dirPath);
            dir.mkdir();
        }
    }

    public static void updateDir(String oldName, String newName) {
        String dirPath = UPLOAD_PATH_PREFIX + oldName + "/";
        String newPath = UPLOAD_PATH_PREFIX + newName + "/";

        System.out.println("updateDir ----> old dirPth : " + dirPath + " , newPath : " + newPath);

        File dir = new File(dirPath);
        if (!dir.exists()) {
            System.out.println("旧的 dir 不存在, 直接按照新的名字新建文件夹!");
            dir = new File(newPath);
            dir.mkdir();
        } else {
            System.out.println("dir 开始重命名, newPath : " + newPath);
            dir.renameTo(new File(newPath));
        }
    }



    public static List<File> getAllFile(String path) {
        List<File> allfilelist = new ArrayList<File>();
        return getAllFile(new File(path), allfilelist);
    }

    public static List<File> getAllFile(File file, List<File> allfilelist) {
        if (file.exists()) {
            //判断文件是否是文件夹，如果是，开始递归
            if (file.isDirectory()) {
                File f[] = file.listFiles();
                for (File file2 : f) {
                    getAllFile(file2, allfilelist);
                }
            } else {
                allfilelist.add(file);
            }
        }
        return allfilelist;
    }

    public static List<File> getAllAvaliableFile(File file, List<File> allfilelist) {
        if (file.exists()) {
            //判断文件是否是文件夹，如果是，开始递归
            if (file.isDirectory()) {
                File f[] = file.listFiles();
                for (File file2 : f) {
                    getAllAvaliableFile(file2, allfilelist);
                }
            } else {
                String fileName = file.getName();
                // 后缀名
                String postFix = fileName.substring(fileName.lastIndexOf("."));
                if (isPdf(postFix) || isVideo(postFix) || isPicture(postFix)) {
                    allfilelist.add(file);
                }
            }
        }
        return allfilelist;
    }

    /**
     * 复制单个文件
     *
     * @param oldPath
     * @param newPath
     */
    public static void copyfile(String oldPath, String newPath) {
        int f;
        File oldFile = new File(oldPath);
        if (oldFile.exists()) {
            FileInputStream fis = null;
            FileOutputStream fos = null;
            try {
                //读入原文件
                fis = new FileInputStream(oldFile);
                fos = new FileOutputStream(newPath);

                byte[] buffer = new byte[1024];
                //当文件没有读到结尾
                while ((f = fis.read(buffer)) != -1) {
                    fos.write(buffer, 0, f);
                }
            } catch (Exception e) {
                System.err.println("复制单个文件操作出错!");
                e.printStackTrace();
            } finally {
                IOUtils.closeQuietly(fis);
                IOUtils.closeQuietly(fos);
            }
        }
    }


    @SuppressWarnings("Duplicates")
    public static void main(String[] args) throws Exception {
        // 浏览器可以直接预览图片和.txt文档
        // http://118.24.41.50:8083/file/12-file--test/88-file1/1.jpg
//        File file = new File("G:\\lxw\\aaaa");
//        delAllFile(file);
        //System.out.println("JEPG".toLowerCase());
        //System.out.println(file.getAbsolutePath());
        //String fileName = file.getName();
        // 后缀名
        //String postFix = fileName.substring(fileName.lastIndexOf(".") + 1);
        //System.out.println(postFix);
        //unzip(new File("G:\\lxw\\新建文件夹.zip"), new File("G:\\aa\\"));
        //zipUncompress("G:\\lxw\\新建文件夹.zip", "G:\\aa\\");
        //File file = new File("G:\\aa\\1.txt");
        //file.createNewFile();
        //unZipFiles(new File("G:\\aa\\新建文件夹.zip"), "G:\\aa\\");
        //unzipOperate("G:\\aa\\新建文件夹.zip", "G:\\aa\\新建文件夹", "G:\\aa\\");
        //Thread.sleep(30000);
        //delAllFile(new File("G:\\aa\\新建文件夹"));
        //delAllFile(new File("G:\\aa\\新建文件夹.zip"));
        File file = new File("G:\\lxw\\1.lxw-评审打分汇总表.xls");
        if (file.exists()) {
            System.out.println("file cunzai");
        }
    }


    /**
     * zip压缩包的解压
     *
     * @param zipFile
     * @param descDir
     */
    public static void unzip(File zipFile, File descDir) {
        try (ZipArchiveInputStream inputStream = getZipFile(zipFile)) {
            // 解压路径
            if (!descDir.exists()) {
                descDir.mkdirs();
            }
            ZipArchiveEntry entry;
            while ((entry = inputStream.getNextZipEntry()) != null) {
                if (entry.isDirectory()) {
                    File directory = new File(descDir, entry.getName());
                    directory.mkdirs();
                } else {
                    OutputStream os = null;
                    try {
                        os = new BufferedOutputStream(new FileOutputStream(new File(descDir, entry.getName())));
                        //输出文件路径信息
                        System.out.println("解压文件的当前路径为 : descDir : " + descDir + " , name : " + entry.getName());
                        IOUtils.copy(inputStream, os);
                    } finally {
                        IOUtils.closeQuietly(os);
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static ZipArchiveInputStream getZipFile(File zipFile) throws Exception {
        return new ZipArchiveInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
    }


    /**
     * 解压文件到指定目录
     * 解压后的文件名，和之前一致
     * @param zipFile	待解压的zip文件
     * @param descDir 	指定目录
     */
    public static void unZipFiles(File zipFile, String descDir) {
        // 解决中文文件夹乱码
        ZipFile zip = null;
        try {
            zip = new ZipFile(zipFile, Charset.forName("GBK"));
        } catch (IOException e1) {
            e1.printStackTrace();
        }

        String name = zip.getName().substring(zip.getName().lastIndexOf('/') + 1, zip.getName().lastIndexOf('.'));

        File pathFile = new File(descDir + name);

        if (!pathFile.exists()) {
            pathFile.mkdirs();
        }

        for (Enumeration<? extends ZipEntry> entries = zip.entries(); entries.hasMoreElements();) {
            InputStream in = null;
            FileOutputStream out = null;
            ZipEntry entry = (ZipEntry) entries.nextElement();
            String zipEntryName = entry.getName();
            try {
                in = zip.getInputStream(entry);
                String outPath = (descDir + name + "/" + zipEntryName).replaceAll("\\*", "/");

                // 判断路径是否存在,不存在则创建文件路径
                File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
                if (!file.exists()) {
                    file.mkdirs();
                }
                // 判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
                if (new File(outPath).isDirectory()) {
                    continue;
                }
                // 输出文件路径信息
                System.out.println("解压文件的当前路径为 : " + outPath);

                out = new FileOutputStream(outPath);

                IOUtils.copy(in, out);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IOUtils.closeQuietly(in);
                IOUtils.closeQuietly(out);
            }
        }
        return;
    }


    /**
     * .rar压缩包的解压，系统暂时不支持此文件类型的解压
     *
     * @param rarFile
     * @param outDir
     * @throws Exception
     */
    public static void unrar(File rarFile, String outDir) throws Exception {
        File outFileDir = new File(outDir);
        if (!outFileDir.exists()) {
            boolean isMakDir = outFileDir.mkdirs();
            if (isMakDir) {
                System.out.println("创建压缩目录成功");
            }
        }
        Archive archive = new Archive(new FileInputStream(rarFile));
        FileHeader fileHeader = archive.nextFileHeader();
        while (fileHeader != null) {
            if (fileHeader.isDirectory()) {
                fileHeader = archive.nextFileHeader();
                continue;
            }
            File out = new File(outDir + fileHeader.getFileNameString());
            if (!out.exists()) {
                if (!out.getParentFile().exists()) {
                    out.getParentFile().mkdirs();
                }
                out.createNewFile();
            }
            FileOutputStream os = new FileOutputStream(out);
            archive.extractFile(fileHeader, os);

            os.close();

            fileHeader = archive.nextFileHeader();
        }
        archive.close();
    }


    public static List<File> getAllAvaliableFile(String path) {
        List<File> res = new ArrayList<>();
        return getAllAvaliableFile(new File(path), res);
    }



}